package com.skiving.cloud.workflow.service.impl;

import com.mybatisflex.core.paginate.Page;
import com.skiving.cloud.auth.entity.AuthUser;
import com.skiving.cloud.auth.util.UserUtil;
import com.skiving.cloud.common.exception.BusinessException;
import com.skiving.cloud.common.utils.PageParameter;
import com.skiving.cloud.workflow.entity.ProcessDefinitionEntity;
import com.skiving.cloud.workflow.entity.ProcessXmlVo;
import com.skiving.cloud.workflow.service.ModelService;
import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.repository.ProcessDefinitionQuery;
import org.camunda.bpm.model.bpmn.Bpmn;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
import org.camunda.bpm.model.bpmn.instance.Definitions;
import org.camunda.bpm.model.bpmn.instance.StartEvent;
import org.noear.solon.Solon;
import org.noear.solon.Utils;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.noear.solon.core.handle.UploadedFile;
import org.noear.solon.core.util.IoUtil;
import org.noear.solon.data.annotation.Cache;
import org.noear.solon.data.annotation.CacheRemove;
import org.noear.solon.data.annotation.Transaction;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 流程模型管理
 */
@Component
public class ModelServiceImpl implements ModelService {
    
    @Inject
    private RepositoryService repositoryService;
    /**
     * 分页查询模型
     *
     * @param parameter 参数
     * @param user 用户
     * @return 流程列表
     */
    @Override
    public Page<ProcessDefinitionEntity> list(PageParameter<Map<String, Object>> parameter, AuthUser user){
        String searchName = parameter.getParam().get("searchName") == null ? "" : parameter.getParam().get("searchName").toString();
        int pageSize = parameter.getPageSize();
        int page = parameter.getPage();
        int offset = (page - 1) * pageSize;
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
        if(Utils.isNotBlank(searchName)){
            query = query.processDefinitionNameLike("%" + searchName + "%");
        }
        if(!user.getTenantId().equals("1")){
            //非超级管理员租户只能查自己的流程
            query = query.tenantIdIn(user.getTenantId());
        }
        List<ProcessDefinition> list = query.latestVersion()
                .orderByDeploymentTime().desc().listPage(offset, pageSize);
        int total = (int) query.latestVersion().count();
        List<ProcessDefinitionEntity> listEn = list.stream().map(m -> {
            ProcessDefinitionEntity p = new ProcessDefinitionEntity();
            p.setId(m.getId());
            p.setName(m.getName());
            p.setDescription(m.getDescription());
            p.setKey(m.getKey());
            p.setVersion(m.getVersion());
            p.setCategory(m.getCategory());
            p.setDeploymentId(m.getDeploymentId());
            p.setResourceName(m.getResourceName());
            p.setDiagramResourceName(m.getDiagramResourceName());
            p.setHasStartFormKey(m.hasStartFormKey());
            p.setVersionTag(m.getVersionTag());
            p.setHistoryTimeToLive(m.getHistoryTimeToLive());
            p.setStartableInTasklist(m.isStartableInTasklist());
            p.setTenantId(m.getTenantId());
            return p;
        }).toList();
        return new Page<>(listEn, page, pageSize, total);
    }
    
    /**
     * 根据id加载模型数据
     *
     * @param processDefinitionId 流程定义id
     * @return 流程模型
     */
    @Override
    public BpmnModelInstance getEditorJson(String processDefinitionId) {
        return repositoryService.getBpmnModelInstance(processDefinitionId);
    }
    
    /**
     * 获取最新版的流程定义
     *
     * @return 流程定义
     */
    @Override
    public List<ProcessDefinition> getProcessOption(){
        return repositoryService.createProcessDefinitionQuery().latestVersion().list();
    }
    
    /**
     * 发布模型
     *
     * @param id 模型id
     * @param user 用户
     */
    @Override
    @Transaction
    @CacheRemove(tags = "skivingProcess")
    public void deploy(String id, AuthUser user) {
        ProcessDefinition p = repositoryService.getProcessDefinition(id);
        InputStream inp = repositoryService.getProcessModel(id);
        repositoryService.createDeployment().name(p.getName()).addInputStream(p.getResourceName(),inp).deploy();
    }
    
    /**
     * 加载流程文件内容
     *
     * @param processDefinitionId 流程定义id
     * @return 流程文件内容
     */
    @Override
    public InputStream resourceRead(String processDefinitionId){
        return repositoryService.getProcessModel(processDefinitionId);
    }
    
    /**
     * 流程导入
     *
     * @param files 文件
     * @param user 用户
     */
    @Override
    @Transaction
    @CacheRemove(tags = "skivingProcess")
    public void importDeploy(List<UploadedFile> files, AuthUser user){
        for (UploadedFile file : files) {
            if(file.getName() != null){
                String fileName = file.getName();
                InputStream fileInputStream = file.getContent();
                String extension = file.getExtension();
                if(extension.equals("zip") || extension.equals("bar") || extension.equals("png") || extension.equals("bpmn")){
                    repositoryService.createDeployment().name(fileName).addInputStream(fileName, fileInputStream).deploy();
                }else{
                    throw new BusinessException("不支持的文件类型 ");
                }
            }
        }
    }
    
    /**
     * 获取开始节点信息用于组织页面表单
     *
     * @param defKey 节点编码
     * @return 开始节点
     */
    @Override
    public StartEvent getModelStartNodeInfo(String defKey){
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(defKey).latestVersion().singleResult();
        BpmnModelInstance bpmnModelInstance = repositoryService.getBpmnModelInstance(processDefinition.getId());
        return bpmnModelInstance.getModelElementsByType(StartEvent.class).stream().findFirst().orElseThrow();
    }
    
    /**
     * 根据id获取流程文件xml
     *
     * @param id 流程定义id
     * @return 流程文件xml
     */
    @Override
    public String getResourceByDefId(String id) throws IOException{
        InputStream in = repositoryService.getProcessModel(id);
        return IoUtil.transferToString(in, Solon.encoding());
    }
    
    /**
     * 保存流程设计
     *
     * @param xmlVo 流程设计内容
     * @param user  用户
     */
    @Override
    @Transaction
    @CacheRemove(tags = "skivingProcess")
    public void saveProcessDefinition(ProcessXmlVo xmlVo, AuthUser user) {
        InputStream in = new ByteArrayInputStream(xmlVo.getXml().getBytes(StandardCharsets.UTF_8));
        BpmnModelInstance modelInstance = Bpmn.readModelFromStream(in);
        Definitions definitions = modelInstance.getDefinitions();
        String tenantId = definitions.getDomElement().getChildElements().get(0).getAttribute("http://camunda.org/schema/1.0/bpmn","tenantId");
        if(Utils.isBlank(tenantId)){
            tenantId = user.getTenantId();
        }
        repositoryService.createDeployment().name(xmlVo.getName())
                .addModelInstance(xmlVo.getName() + ".bpmn", modelInstance)
                .tenantId(tenantId)
                .deploy();
    }
    
    /**
     * 查询模型历史记录列表
     *
     * @param parameter 参数
     * @return 分页结果
     */
    @Override
    public Page<ProcessDefinitionEntity> getModelDeployHisList(PageParameter<Map<String, Object>> parameter){
        int pageSize = parameter.getPageSize();
        int page = parameter.getPage();
        int offset = (page - 1) * pageSize;
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
        if(Utils.isNotBlank(parameter.getParam().get("key").toString())){
            query = query.processDefinitionKey(parameter.getParam().get("key").toString());
        }
        List<ProcessDefinition> list = query.latestVersion()
                .orderByDeploymentTime().desc().listPage(offset, pageSize);
        int total = (int) query.latestVersion().count();
        List<ProcessDefinitionEntity> listEn = list.stream().map(m -> {
            ProcessDefinitionEntity p = new ProcessDefinitionEntity();
            p.setId(m.getId());
            p.setName(m.getName());
            p.setDescription(m.getDescription());
            p.setKey(m.getKey());
            p.setVersion(m.getVersion());
            p.setCategory(m.getCategory());
            p.setDeploymentId(m.getDeploymentId());
            p.setResourceName(m.getResourceName());
            p.setDiagramResourceName(m.getDiagramResourceName());
            p.setHasStartFormKey(m.hasStartFormKey());
            p.setVersionTag(m.getVersionTag());
            p.setHistoryTimeToLive(m.getHistoryTimeToLive());
            p.setStartableInTasklist(m.isStartableInTasklist());
            
            return p;
        }).toList();
        return new Page<>(listEn, page, pageSize, total);
    }
    
    /**
     * 根据部署id获取流程定义
     *
     * @param deploymentId 部署编号
     * @return 流程定义
     */
    @Override
    public ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId){
        return repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
    }
    
    /**
     * 删除一个模型
     *
     * @param id 流程定义id
     */
    @Override
    @CacheRemove(tags = "skivingProcess")
    public void deleteModel(String id){
        repositoryService.deleteProcessDefinition(id);
    }
    
    /**
     * 获取流程信息
     *
     * @return {key:流程定义编码,value:流程名称}
     */
    @Override
    public List<Map<String, String>> getProcessDefKV(){
        return repositoryService.createProcessDefinitionQuery().latestVersion().list().stream()
                .map(m -> {
                    Map<String, String> map = new HashMap<>();
                    map.put("key", m.getKey());
                    map.put("value", m.getName());
                    return map;
                }).toList();
    }

    /**
     * 获取所以流程定义
     *
     * @return 流程定义列表
     */
    @Override
    @Cache(key = "skivingProcess:getAllProcessDefinitionEntity", tags = "skivingProcess")
    public List<ProcessDefinitionEntity> getAllProcessDefinitionEntity() {
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
        if(!UserUtil.getCurrentUser().getTenantId().equals("1")){
            //非超级管理员租户只能查自己的流程
            query = query.tenantIdIn(UserUtil.getCurrentUser().getTenantId());
        }
        List<ProcessDefinition> list = query.latestVersion()
                .orderByDeploymentTime().desc().list();
        return list.stream().map(m -> {
            ProcessDefinitionEntity p = new ProcessDefinitionEntity();
            p.setId(m.getId());
            p.setName(m.getName());
            p.setDescription(m.getDescription());
            p.setKey(m.getKey());
            p.setVersion(m.getVersion());
            p.setCategory(m.getCategory());
            p.setDeploymentId(m.getDeploymentId());
            p.setResourceName(m.getResourceName());
            p.setDiagramResourceName(m.getDiagramResourceName());
            p.setHasStartFormKey(m.hasStartFormKey());
            p.setVersionTag(m.getVersionTag());
            p.setHistoryTimeToLive(m.getHistoryTimeToLive());
            p.setStartableInTasklist(m.isStartableInTasklist());
            p.setTenantId(m.getTenantId());
            return p;
        }).toList();
    }
}
